package com.dyb.nio.reactor;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 反应器
 * 1.创建一个socketChanel连接，selector监听accept事件，设置处理事件的acceptHandle
 * 2.循环体-阻塞获取selector监听事件，交给绑定的handle处理
 *  (第一次：Handle获取到channel，创建IOHandle，IOHandle拿到acceptSocketChannel，重新注册selector，重新监听read事件，设置处理事件的IOHandle
 *  之后一直执行这两步
 *  [
 *      第二次：IOHandle从acceptSocketChannel读取数据，重新设置监听write事件
 *      第三次：IOHandle向acceptSocketChannel写入数据，重新设置监听read事件
 *      ....
 *  ]
 *  )
 *
 */
/**
 * 反应器
 * @author dyb
 * @date 2020/6/19
 */
public class Reactor implements Runnable {
    ServerSocketChannel serverSocketChannel;
    Selector selector;

    Reactor() throws IOException {
        serverSocketChannel = ServerSocketChannel.open();

        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8888);
        serverSocketChannel.socket().bind(inetSocketAddress);
        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);

        selector = Selector.open();
        // 监听连接
        SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        selectionKey.attach(new AcceptHandle());
    }

    @Override
    public void run() {

        while (!Thread.interrupted()) {
            try {
                // 阻塞等待响应
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    Runnable runnable = (Runnable)key.attachment();
                    runnable.run();
                }
                selectionKeys.clear();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 新连接接入会执行一次，初始化IOHandle，后续监听读写事件
     */
    class AcceptHandle implements Runnable {
        @Override
        public void run() {
            try {
                SocketChannel accept = serverSocketChannel.accept();
                new IOHandle(accept, selector);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new Thread(new Reactor()).start();
    }

}
